package com.jifeng.api.annotation.aop;

import cn.hutool.core.convert.Convert;
import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import com.jifeng.api.annotation.AccessLimitAnnotation;
import com.jifeng.api.constant.HttpStatusEnum;
import com.jifeng.api.crud.entity.TerminalEntity;
import com.jifeng.api.crud.service.TerminalService;
import com.jifeng.api.exception.CustomException;
import com.jifeng.api.vo.ReqRentVo;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import java.lang.reflect.Method;
import java.util.Objects;
import java.util.concurrent.TimeUnit;

/**
 * @author JiFeng
 */
@Aspect
@Configuration
@Slf4j
public class AccessLimitInterceptor {

    @Autowired
    TerminalService terminalService;

    /**
     * 最大缓存个数
     */
    private static final Integer MAXIMUM_SIZE = 100;

    /**
     * 缓存过期时间
     */
    private static final Integer EXPIRE_TIME = 60;

    /**
     * 设置缓存
     */
    private static final Cache<String, Object> CACHE = CacheBuilder.newBuilder()
            //设置最大缓存个数
            .maximumSize(MAXIMUM_SIZE)
            //设置缓存过期时间
            .expireAfterWrite(EXPIRE_TIME, TimeUnit.SECONDS)
            .build();

    /**
     * 拦截器
     *
     * @param pjp 拦截对象
     * @return 对象
     */
    @Around("execution(public  * *(..))&& @annotation(com.jifeng.api.annotation.AccessLimitAnnotation)")
    public Object interceptor(ProceedingJoinPoint pjp) {
        String terminalId ="";
        try {
            // 获取签名
            MethodSignature signature = (MethodSignature) pjp.getSignature();

            // 从签名中获取方法
            Method method = signature.getMethod();

            // 从方法获取注解
            AccessLimitAnnotation accessLimit = method.getAnnotation(AccessLimitAnnotation.class);

            // 设置 Key
            String key = getKey(accessLimit.key(), pjp.getArgs());

            // 获取机柜编号
            terminalId = getTerminalId(pjp.getArgs());

            // 判断key是否存在
            if (CACHE.getIfPresent(key) != null) {
                Integer count = Convert.toInt(CACHE.getIfPresent(key));

                // key存在判断是否超过了次数
                if (count >= accessLimit.maxCount()) {

                    /**
                     * 超出访问次数，将机柜设置为空闲并抛出异常
                     */
                    log.warn("path：{}，errMsg：{}", getRequestUrl(), "超出访问次数，将机柜设置为空闲");

                    TerminalEntity terminalEntity = new TerminalEntity();
                    terminalEntity.setIot(terminalId);
                    terminalEntity.setRentState(0);
                    boolean update = terminalService.updateById(terminalEntity);
                    if (!update) {
                        log.warn("path：{}，errMsg：{}", getRequestUrl(), "更新机柜状态失败");
                    }

                    throw new CustomException(HttpStatusEnum.HTTP_ACCESS_LIMIT);
                }

                // 未超过异常进行值加1
                CACHE.put(key, count + 1);
            } else {
                // key不存在进行设置为1
                CACHE.put(key, 1);
            }
            return pjp.proceed();
        } catch (Throwable throwable) {
            /**
             * 出现异常将机柜设置为空闲并抛出异常
             */
            TerminalEntity terminalEntity = new TerminalEntity();
            terminalEntity.setIot(terminalId);
            terminalEntity.setRentState(0);
            boolean update = terminalService.updateById(terminalEntity);
            if (!update) {
                log.warn("path：{}，errMsg：{}", getRequestUrl(), "更新机柜状态失败");
            }
            throwable.printStackTrace();
            throw new CustomException(HttpStatusEnum.HTTP_SYSTEM_ERROR);
        }
    }

    /**
     * key的生成策略
     *
     * @param key  键的过期时间
     * @param args 请求参数
     * @return 键
     */
    private String getKey(String key, Object[] args) {
        if (args[0] instanceof ReqRentVo) {
            ReqRentVo reqRentVo = (ReqRentVo) args[0];
            return key + ":" + reqRentVo.getMobile();
        }
        return key;
    }

    private String getTerminalId(Object[] args) {
        if (args[0] instanceof ReqRentVo) {
            ReqRentVo reqRentVo = (ReqRentVo) args[0];
            return reqRentVo.getTerminalId();
        }
        return "";
    }

    public String getRequestUrl() {
        return ((ServletRequestAttributes) Objects.requireNonNull(RequestContextHolder.getRequestAttributes())).getRequest().getRequestURI();
    }
}
